﻿using BCCommon;
using BCCommon.PaymentEnums;
using Microsoft.Extensions.Configuration;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Xml;

namespace BCService.Common.Payment.Payment.WeixinPay
{
    /// <summary>
    /// 微信支付组件
    /// </summary>
    public class WeiXinPayPlug : IWeiXinPayPlug
    {
        private readonly IConfiguration _configuration;
        private readonly IWeiXinPayPlugConfig _wxConfig;
        public SourceAppType? AppType { get; set; }
        public WeiXinPayPlug(IConfiguration configuration,IWeiXinPayPlugConfig wxConfig)
        {
            _configuration = configuration;
            _wxConfig = wxConfig;
        }
        private SortedDictionary<string, object> dic_values = new SortedDictionary<string, object>();

        /// <summary>
        /// 设置键值
        /// </summary>
        /// <param name="key">键</param>
        /// <param name="value">值</param>
        public void SetValue(string key, object value)
        {
            dic_values[key] = value;
        }
        /// <summary>
        /// 清理键值
        /// </summary>
        public void ClearAllValue()
        {
            dic_values = new SortedDictionary<string, object>();
        }
        /// <summary>
        /// 获取某个键值
        /// </summary>
        /// <param name="key">键</param>
        /// <returns></returns>
        public object GetValue(string key)
        {
            object o = null;
            dic_values.TryGetValue(key, out o);
            return o;
        }

        /// <summary>
        ///判断该键值是否已设置/存在
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public bool IsSet(string key)
        {
            object o = null;
            dic_values.TryGetValue(key, out o);
            //if (null != o)
            //    return true;
            //else
            //    return false;
            return null != o ? true : false;
        }

        /// <summary>
        /// 将字典数据转换为string类型XML
        /// </summary>
        /// <returns></returns>
        public string FromDictionaryToXml()
        {
            //数据为空时不能转化为xml格式
            if (0 == dic_values.Count)
            {
                throw new ArgumentException("参数数据为空，不可转换为XML数据");
            }

            return JsonConvert.DeserializeXNode(JsonConvert.SerializeObject(dic_values), "xml").ToString();
        }
        /// <summary>
        /// 将字典转换为URL参数(例如：a=1 b=2)
        /// </summary>
        /// <returns></returns>
        public string FromDictionaryToUrlParameters()
        {
            return string.Join("&", dic_values.Select(p => string.Format("{0}={1}", p.Key, WebUtility.UrlEncode(p.Value.ToString()))));
        }
        /// <summary>
        /// 将string类型的XML转换为字典数据
        /// </summary>
        /// <param name="xml"></param>
        /// <returns></returns>
        public SortedDictionary<string, object> FromXmlToDictionary(string xml)
        {
            dic_values = new SortedDictionary<string, object>();
            if (string.IsNullOrEmpty(xml))
            {
                throw new ArgumentException("无法将空/NULL转换为SortedDictionary!");
            }
            //XML文档构造参数数据等
            XmlDocument xmlDoc = new XmlDocument();
            xmlDoc.LoadXml(xml);
            XmlNode xmlNode = xmlDoc.FirstChild;//获取到根节点<xml>
            XmlNodeList nodes = xmlNode.ChildNodes;
            foreach (XmlNode xn in nodes)
            {
                XmlElement xe = (XmlElement)xn;
                dic_values[xe.Name] = xe.InnerText;//获取xml的键值对到内部的数据中
            }

            try
            {
                if (dic_values["return_code"].ToString() != "SUCCESS")
                {
                    throw new ArgumentException("微信支付请求失败！" + JsonConvert.SerializeObject(dic_values));
                    //return dic_values;
                }
                //判断系统错误
                if (IsSet("err_code_des"))
                {
                    throw new ArgumentException(dic_values["err_code_des"].ToString());
                }
                //验证签名是否一致
                ValidateSign();
            }
            catch (Exception ex)
            {
                throw new ArgumentException(ex.Message);
            }
            return dic_values;
        }
        /// <summary>
        /// 验签
        /// </summary>
        /// <returns></returns>
        public bool ValidateSign()
        {
            //判断是否签名
            if (IsSet("sign"))
            {
                //接收到的签名
                string return_sign = GetValue("sign").ToString();

                if (!_wxConfig.initDone)
                {
                    if (AppType.HasValue)
                    {
                        _wxConfig.InitConfig(AppType, null);
                    }
                    else
                    {
                        _wxConfig.InitConfig(null, GetValue("appid").ToString());
                    }
                }

                //算出新的签名
                string cal_sign = MakeSign();

                if (cal_sign == return_sign)
                {
                    return true;
                }
                //签名不一致抛出异常
                throw new ArgumentException("签名验证错误!");
            }
            return true;
        }

        /// <summary>
        /// 生成签名
        /// </summary>
        /// <returns></returns>
        public string MakeSign()
        {
            bool exceiption = false;
            int index = 0;
            List<int> exceptionIndex = new List<int>();
            foreach (var item in dic_values)
            {
                if (item.Value == null)
                {
                    exceiption = true;
                    exceptionIndex.Add(index);
                }
                index++;
            }

            if (exceiption)
            {
                DirectoryInfo directoryInfo = new DirectoryInfo(Directory.GetCurrentDirectory());

                if (!Directory.Exists(Path.Combine(directoryInfo.FullName, "WeiXinPayException")))
                {
                    Directory.CreateDirectory(Path.Combine(directoryInfo.FullName, "WeiXinPayException"));
                }
                FileInfo fileInfo = new FileInfo(Path.Combine(directoryInfo.FullName, "WeiXinPayException", DateTime.Now.ToString("yyyy-MM-dd") + ".txt").ToString());

                using (StreamWriter sw = fileInfo.CreateText())
                {
                    sw.WriteLine("*****************************************");
                    sw.WriteLine("当前时间：{0}", DateTime.Now.ToString());
                    sw.WriteLine("序列化参数:");
                    foreach (var item in dic_values)
                    {
                        sw.WriteLine(string.Format("{0},{1}", item.Key, item.Value));
                    }
                    sw.WriteLine();
                    sw.WriteLine("异常参数索引:");
                    foreach (var item in exceptionIndex)
                    {
                        sw.WriteLine("{0}", item);
                    }
                    sw.WriteLine();
                }
            }
            //where Key != "sign" 用于排除返回参数中的签名验证返回参数用
            //string value = $"{string.Join("&", dic_values.Where(p => p.Key != "sign" && p.Value.ToString().Length != 0).Select(d => string.Concat(d.Key, "=", d.Value)))}&key={_wxConfig.KEY}";
            //return HashHelper.MD5(value);
            IEnumerable<string> values = dic_values.Where(p => p.Key != "sign" && p.Value.ToString().Length != 0).Select(d => string.Concat(d.Key, "=", d.Value));
            return HashHelper.MD5($"{string.Join("&", values)}&key={_wxConfig.KEY}");
        }

        /// <summary>
        /// 生成时间戳
        /// </summary>
        /// <returns></returns>
        public string GenerateTimeStamp()
        {
            TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0);
            return Convert.ToInt64(ts.TotalSeconds).ToString();
        }

        /// <summary>
        ///必选先构造过参数 获取字典
        /// </summary>
        /// <returns></returns>
        public SortedDictionary<string, object> GetDictionary()
        {
            return dic_values;
        }

        /// <summary>
        /// 构造完参数后调用此方法
        /// </summary>
        /// <param name="url">访问地址</param>
        /// <param name="isCertRequest">证书访问请求</param>
        /// <returns></returns>
        public SortedDictionary<string, object> PostRequest(string url, bool isCertRequest = false)
        {
            SortedDictionary<string, object> dic = new SortedDictionary<string, object>();
            HttpClientHandler handler = new HttpClientHandler();
            if (isCertRequest)
            {
                handler.ClientCertificateOptions = ClientCertificateOption.Manual;
                handler.SslProtocols = SslProtocols.Tls12;
                //指定证书和路径
                handler.ClientCertificates.Add(new X509Certificate2(_wxConfig.CERT_PATH, _wxConfig.CERT_PASSWORD));
            }
            using (HttpClient httpClient = new HttpClient(handler))
            {
                httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("text/xml"));
                using (HttpResponseMessage responseMessage = httpClient.PostAsync(url, new StringContent(FromDictionaryToXml())).Result)
                {
                    string response = responseMessage.EnsureSuccessStatusCode().Content.ReadAsStringAsync().GetAwaiter().GetResult();
                    dic = FromXmlToDictionary(response);
                }
            }
            return dic;
        }
        /// <summary>
        /// Get请求
        /// </summary>
        /// <param name="url"></param>
        /// <returns></returns>
        public SortedDictionary<string, object> GetRequest(string url)
        {
            string requestUrl = string.Format("{0}?{1}", url, this.FromDictionaryToUrlParameters());
            using (HttpClient httpClient = new HttpClient())
            {
                using (HttpResponseMessage responseMessage = httpClient.GetAsync(requestUrl).Result)
                {
                    string jsonString = responseMessage.EnsureSuccessStatusCode().Content.ReadAsStringAsync().GetAwaiter().GetResult();
                    //dynamic result = JsonConvert.DeserializeObject(jsonString);
                    return JsonConvert.DeserializeObject<SortedDictionary<string, object>>(jsonString);
                }
            }
        }
    }
}
